﻿//
//                                                                                 (C) SocketCoder.Com 
// Note: This Project is just some simple samples, if you are looking for a production solution Visit 
// SocketCoder.Com to Buy The Full WCS Web Conferencing Solution Source Code. 
//
namespace VoiceEncoder
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Threading;
    using System.Windows.Media;

    public class StreamingMediaSource : MediaStreamSource
    {

        private Stream stream;

        private object locker = new object();

        private ManualResetEvent BufferReadEvent = new ManualResetEvent(false);
        private ManualResetEvent BufferWriteEvent = new ManualResetEvent(false);

        SpeexEncoderDecoder decoder = new SpeexEncoderDecoder();


        int BitPerSample;
        int SamplePeerSecond;
        int Channels;

        public Queue<byte[]> JitterBufferQueue = new Queue<byte[]>();

        private MediaStreamDescription audioDesc;
        string HexWaveHeader;

        public StreamingMediaSource(int _BitPerSample, int _SamplePeerSecond, int _Channels)
        {
            BitPerSample = _BitPerSample;
            SamplePeerSecond = _SamplePeerSecond;
            Channels = _Channels;

            HexWaveHeader = WaveHeaderToHexString(BitPerSample, SamplePeerSecond, Channels);
        }

        protected override void OpenMediaAsync()
        {
            try
            {
                // Init
                Dictionary<MediaStreamAttributeKeys, string> streamAttributes = new Dictionary<MediaStreamAttributeKeys, string>();
                Dictionary<MediaSourceAttributesKeys, string> sourceAttributes = new Dictionary<MediaSourceAttributesKeys, string>();
                List<MediaStreamDescription> availableStreams = new List<MediaStreamDescription>();

                // Stream Description
                streamAttributes[MediaStreamAttributeKeys.CodecPrivateData] = HexWaveHeader;
                this.audioDesc = new MediaStreamDescription(MediaStreamType.Audio, streamAttributes);
                availableStreams.Add(this.audioDesc);
                sourceAttributes[MediaSourceAttributesKeys.Duration] = "0";
                ReportOpenMediaCompleted(sourceAttributes, availableStreams);
            }
            catch (Exception) { }
        }

        protected override void GetSampleAsync(MediaStreamType mediaStreamType)
        {
            stream = ReadBufferQueue();
            MediaStreamSample sample = new MediaStreamSample(this.audioDesc, stream, 0, stream.Length, 0, 10, new Dictionary<MediaSampleAttributeKeys, string>());
            base.ReportGetSampleCompleted(sample);
        }

        protected override void SeekAsync(long seekToTime)
        {
            ReportSeekCompleted(seekToTime);
        }

        public MemoryStream ReadBufferQueue()
        {
            while (this.JitterBufferQueue.Count == 0)
            {
                this.BufferWriteEvent.Reset();
                this.BufferWriteEvent.WaitOne(10);
            }
            lock (this.locker)
            {
                byte[] DecodedBuffer = decoder.Decode(this.JitterBufferQueue.Dequeue(), SamplePeerSecond, Channels);

                return new MemoryStream(DecodedBuffer);
            }
        }

        public void PutBufferToQueue(byte[] data)
        {
            lock (this.locker)
            {
                while (this.JitterBufferQueue.Count > 40)
                {
                    this.JitterBufferQueue.Dequeue();
                }
                this.JitterBufferQueue.Enqueue(data);
                this.BufferWriteEvent.Set();
            }
        }

        // Define The Wave Header
        public string WaveHeaderToHexString(int BitPerSample, int SamplePeerSecond, int Channels)
        {
            string WaveHeader = string.Empty;
            WaveHeader += ToLittleEndianString(string.Format("{0:X4}", 1));
            WaveHeader += ToLittleEndianString(string.Format("{0:X4}", Channels));
            WaveHeader += ToLittleEndianString(string.Format("{0:X8}", SamplePeerSecond));
            WaveHeader += ToLittleEndianString(string.Format("{0:X8}", (BitPerSample * SamplePeerSecond * Channels / 8)));
            WaveHeader += ToLittleEndianString(string.Format("{0:X4}", (ushort)(BitPerSample * Channels / 8)));
            WaveHeader += ToLittleEndianString(string.Format("{0:X4}", BitPerSample));
            WaveHeader += ToLittleEndianString(string.Format("{0:X4}", 0));
            return WaveHeader;
        }

        public static string ToLittleEndianString(string bigEndianString)
        {
            if (bigEndianString == null)
            {
                return string.Empty;
            }

            char[] bigEndianChars = bigEndianString.ToCharArray();

            // Guard
            if (bigEndianChars.Length % 2 != 0)
            {
                return string.Empty;
            }

            int i, ai, bi, ci, di;
            char a, b, c, d;
            for (i = 0; i < bigEndianChars.Length / 2; i += 2)
            {
                // front byte
                ai = i;
                bi = i + 1;

                // back byte
                ci = bigEndianChars.Length - 2 - i;
                di = bigEndianChars.Length - 1 - i;

                a = bigEndianChars[ai];
                b = bigEndianChars[bi];
                c = bigEndianChars[ci];
                d = bigEndianChars[di];

                bigEndianChars[ci] = a;
                bigEndianChars[di] = b;
                bigEndianChars[ai] = c;
                bigEndianChars[bi] = d;
            }
            return new string(bigEndianChars);
        }

        protected override void CloseMedia() { }
        protected override void GetDiagnosticAsync(MediaStreamSourceDiagnosticKind diagnosticKind) { }
        protected override void SwitchMediaStreamAsync(MediaStreamDescription mediaStreamDescription) { }
    }
}
